#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include "MonitorAscensores.h"
#include "Constantes.h"
#include "Logger.h"
#include "Util.h"

#define PROCESS_NAME "MONITOR_ASCENSORES"

using namespace std;

void MonitorAscensores::log(string message) {
	string name = PROCESS_NAME;
	string msg = name + ": " + message;
	logger->loggearLog(msg);
}

void MonitorAscensores::sync(string message) {
	string name = PROCESS_NAME;
	string msg = name + ": " + message;
	logger->loggearSynchro(msg);
}

void MonitorAscensores::debug(string message) {
	string name = PROCESS_NAME;
	string msg = name + ": " + message;
	logger->loggearDebug(msg);
}

MonitorAscensores::MonitorAscensores(int cantidadAscensores, int cantidadPisos,
		int capacidad, int velocidad) {

	this->logger = Logger::getInstance();
	this->cantidadAscensores = cantidadAscensores;
	this->cantidadPisos = cantidadPisos;
	this->capacidad = capacidad;
	this->velocidad = velocidad;
	this->crearSemaforos();
	this->crearMc();
}

void MonitorAscensores::crearSemaforos() {
	this->semAscensores = new Semaforo((char*) SEM_ARCHIVO_ASC,
			(char) SEM_LETRA_ASC, this->cantidadAscensores);
	this->sync("Semáforos para ascensores creados.");
}

void MonitorAscensores::inicializar() {
	this->inicializarSem();
	this->inicializarMc();
}

void MonitorAscensores::inicializarSem() {
	int valoresIniciales[MAXASCENSORES];
	for (int i = 0; i < this->cantidadAscensores; i++)
		valoresIniciales[i] = 1;
	this->semAscensores->inicializar(valoresIniciales);
	this->sync("Semáforos por ascensor inicializados.");
}

void MonitorAscensores::liberarSem() {
	this->semAscensores->eliminar_todos();
	this->sync("Semáforos por ascensor liberados.");
}

int MonitorAscensores::crearMc() {
	this->mcAscensores = new MemoriaCompartida<tascensor>();
	this->mcVecAscensores = new MemoriaCompartida<int>();

	string message = "Total de memoria reservada por ascensor = ";
	message += Util::intToString(sizeof(tascensor));
	this->sync(message);

	int estadoMemoria = this->mcAscensores->crear((char*) MEM_ASCENSOR,
			MEM_LETRA, sizeof(tascensor), this->cantidadAscensores);
	if (estadoMemoria != SHM_OK) {
		this->sync("Error al crear la memoria compartida del ascensor.");
		return ERROR_SHM;
	}

	estadoMemoria = this->mcVecAscensores->crear((char*) MEM_ASCENSOR,
			MEM_LETRA_VEC, sizeof(int),
			this->cantidadAscensores * MAXPISOS + 1);

	if (estadoMemoria != SHM_OK) {
		this->sync(
				"Error al crear la memoria compartida del vector del ascensor.");
		return ERROR_SHM;
	}

	this->sync("Fin creación SHM de los ascensores.");
	return SUCCESS;
}

void MonitorAscensores::inicializarMc() {
	for (int i = 0; i < this->cantidadAscensores; i++) {
		tascensor ascensor;
		ascensor.pid = 0;
		ascensor.peso = this->capacidad;
		ascensor.velocidad = this->velocidad;
		ascensor.estado = ESPERANDO;
		ascensor.abierto = false;
		ascensor.pisoInferior = 0;
		ascensor.pisoActual = 0;
		ascensor.proxPiso = 0;
		ascensor.cantPersonas = 0;
		ascensor.pisoSuperior = this->cantidadPisos - 1;
		ascensor.nroAscensor = i;
		ascensor.proxPiso = i;
		this->mcAscensores->escribirPos(ascensor, i);
		this->sync("Ascensor " + Util::intToString(i) + " inicializado.");
	}

	for (int i = 0; i < (this->cantidadAscensores * MAXPISOS); i++) {
		this->mcVecAscensores->escribirPos(0, i);
		this->sync("Vector " + Util::intToString(i) + " inicializado.");
	}

}

void MonitorAscensores::liberarMc() {
	this->mcAscensores->liberar();
	this->mcVecAscensores->liberar();
	this->sync("Memoria de Ascensores y VecAscensores liberada.");
}

/**
 * Actualiza el piso a false.
 */
void MonitorAscensores::arriboPiso(int nroAscensor, int piso) {
	this->sync("P ---> Semáforo Ascensor " + Util::intToString(nroAscensor));
	this->semAscensores->p(nroAscensor, 1);
	tascensor ascensor = this->mcAscensores->leerPos(nroAscensor);
	ascensor.pisoActual = piso;
	this->mcVecAscensores->escribirPos(0,
			(this->cantidadPisos * nroAscensor) + piso);
	this->mcAscensores->escribirPos(ascensor, nroAscensor);

	this->sync("V ---> Semáforo Ascensor " + Util::intToString(nroAscensor));
	this->semAscensores->v(nroAscensor, 1);
}

/**
 * Setea al ascensor como abierto
 */
void MonitorAscensores::abrir(int nroAscensor) {
	this->sync("P ---> Semáforo Ascensor " + Util::intToString(nroAscensor));
	this->semAscensores->p(nroAscensor, 1);
	tascensor ascensor = this->mcAscensores->leerPos(nroAscensor);
	ascensor.abierto = true;
	this->mcAscensores->escribirPos(ascensor, nroAscensor);
	this->sync("V ---> Semáforo Ascensor " + Util::intToString(nroAscensor));
	this->semAscensores->v(nroAscensor, 1);
}

/**
 * Setea al ascensor como cerrado
 */
void MonitorAscensores::cerrar(int nroAscensor) {
	this->sync("P ---> Semáforo Ascensor " + Util::intToString(nroAscensor));
	this->semAscensores->p(nroAscensor, 1);
	tascensor ascensor = this->mcAscensores->leerPos(nroAscensor);
	ascensor.abierto = false;
	this->mcAscensores->escribirPos(ascensor, nroAscensor);
	this->sync("V ---> Semáforo Ascensor " + Util::intToString(nroAscensor));
	this->semAscensores->v(nroAscensor, 1);
}

int MonitorAscensores::buscarAscensor(int pisoOrigen, int pisoDestino) {

	int ascensorBuscado = -1;
	char pestado;
	if (pisoOrigen < pisoDestino)
		pestado = SUBIENDO;
	else
		pestado = BAJANDO;

	for (int nroAscensor = 0;
			((nroAscensor < this->cantidadAscensores) && (ascensorBuscado == -1));
			nroAscensor++) {
		this->sync(
				"P ---> Semáforo Ascensor " + Util::intToString(nroAscensor));
		this->semAscensores->p(nroAscensor, 1);

		tascensor ascensor = this->mcAscensores->leerPos(nroAscensor);

		this->debug(
				"El pisoActual del Ascensor " + Util::intToString(nroAscensor)
						+ " es " + Util::intToString(ascensor.pisoActual)
						+ " y el piso de la Persona es "
						+ Util::intToString(pisoOrigen)
						+ ". Ademas, el ascensor esta abierto? "
						+ Util::intToString(ascensor.abierto));

		if ((ascensor.pisoActual == pisoOrigen) && (ascensor.abierto)
				&& ((ascensor.estado == pestado)
						|| (ascensor.estado == ESPERANDO)))

			ascensorBuscado = nroAscensor;

		this->sync(
				"V ---> Semáforo Ascensor " + Util::intToString(nroAscensor));
		this->semAscensores->v(nroAscensor, 1);
	}
	return ascensorBuscado;
}

/**
 * Lo hace la persona al entrar al ascensor. Es individual.
 * Actualiza la cantidad de personas, el peso disponible, y
 * el piso en que tiene que bajarse a true.
 * @returns el numero de ascensor al que se sube o -1 en
 * 			caso de que no entre.
 */
int MonitorAscensores::subir(int peso, int pisoOrigen, int pisoDestino) {
	int nroAscensor = buscarAscensor(pisoOrigen, pisoDestino);
	if (nroAscensor == -1)
		return -1;
	this->sync("P ---> Semáforo Ascensor " + Util::intToString(nroAscensor));
	this->semAscensores->p(nroAscensor, 1);
	tascensor ascensor = this->mcAscensores->leerPos(nroAscensor);
	if (ascensor.peso - peso >= 0) {
		ascensor.cantPersonas++;
		ascensor.peso -= peso;
		this->mcVecAscensores->escribirPos(1,
				(this->cantidadPisos * nroAscensor) + pisoDestino);
		this->mcAscensores->escribirPos(ascensor, nroAscensor);

	} else
		this->debug("La persona no entra porque supera el peso máximo.");

	this->sync("V ---> Semáforo Ascensor " + Util::intToString(nroAscensor));
	this->semAscensores->v(nroAscensor, 1);
	return nroAscensor;
}

/**
 * Lo hace la persona al bajar del ascensor. Es individual.
 * Actualiza la cantidad de personas, el peso disponible.
 */
void MonitorAscensores::bajar(int nroAscensor, int peso) {
	this->sync("P ---> Semáforo Ascensor " + Util::intToString(nroAscensor));
	this->semAscensores->p(nroAscensor, 1);
	tascensor ascensor = this->mcAscensores->leerPos(nroAscensor);
	ascensor.cantPersonas--;
	ascensor.peso += peso;
	this->mcAscensores->escribirPos(ascensor, nroAscensor);
	this->sync("V ---> Semáforo Ascensor " + Util::intToString(nroAscensor));
	this->semAscensores->v(nroAscensor, 1);
}

/**
 * Lo usa el Ascensor y tal vez el Controlador Central.
 */
tascensor MonitorAscensores::leer(int nroAscensor) {
	this->sync("P ---> Semáforo Ascensor " + Util::intToString(nroAscensor));
	this->semAscensores->p(nroAscensor, 1);
	tascensor ascensor = this->mcAscensores->leerPos(nroAscensor);
	this->sync("V ---> Semáforo Ascensor " + Util::intToString(nroAscensor));
	this->semAscensores->v(nroAscensor, 1);
	return ascensor;
}

/**
 * Lo usa el Ascensor y tal vez el Controlador Central.
 */
void MonitorAscensores::leerVector(int nroAscensor, int vector[]) {
	this->sync("P ---> Semáforo Ascensor " + Util::intToString(nroAscensor));
	this->semAscensores->p(nroAscensor, 1);
	for (int i = 0; i < MAXPISOS; i++) {
		int v = this->mcVecAscensores->leerPos(
				this->cantidadPisos * (nroAscensor) + i);
		vector[i] = v;
		if (v == 1)
			this->debug(
					"El Ascensor " + Util::intToString(nroAscensor)
							+ " tiene que parar en el piso "
							+ Util::intToString(i));
	}
	this->sync("V ---> Semáforo Ascensor " + Util::intToString(nroAscensor));
	this->semAscensores->v(nroAscensor, 1);
}

/**
 * Lo usa el Controlador Central.
 */
void MonitorAscensores::asignarProximoPiso(int nroAscensor, int proximoPiso,
		char estado) {
	this->sync("P ---> Semáforo Ascensor " + Util::intToString(nroAscensor));
	this->semAscensores->p(nroAscensor, 1);
	tascensor ascensor = this->mcAscensores->leerPos(nroAscensor);
	ascensor.proxPiso = proximoPiso;
	ascensor.estado = estado;
	this->mcAscensores->escribirPos(ascensor, nroAscensor);
	this->semAscensores->v(nroAscensor, 1);
}

MonitorAscensores::~MonitorAscensores() {
	this->liberarMc();
	delete (this->semAscensores);
	delete (this->mcAscensores);
	delete (this->mcVecAscensores);
	this->debug("Semaforos y SHMs destruidos.");
}
